// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Author: Markus Gutschke

#ifdef USE_POLAR

#include <errno.h>
#include <polarssl/net.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "check.h"
#include "ssl.h"


struct SSLHandle {
  int         fd;
  ssl_context *ctx;
  ssl_session ssn;
};


struct SSL *newSSL(void) {
  struct SSL *ssl;
  check(ssl = malloc(sizeof(struct SSL)));
  initSSL(ssl);
  return ssl;
}

void initSSL(struct SSL *ssl) {
  havege_init(&ssl->hs);
  check(!ssl_init(&ssl->ctx));
  ssl_set_endpoint(&ssl->ctx, SSL_IS_CLIENT);
  ssl_set_authmode(&ssl->ctx, SSL_VERIFY_NONE);
  ssl_set_rng(&ssl->ctx, havege_rand, &ssl->hs);
  ssl_set_ciphersuites(&ssl->ctx, ssl_default_ciphersuites);
}

void destroySSL(struct SSL *ssl) {
  if (ssl) {
    ssl_free(&ssl->ctx);
  }
}

void deleteSSL(struct SSL *ssl) {
  destroySSL(ssl);
  free(ssl);
}

struct SSLHandle *openSSLHandle(struct SSL *ssl, int fd) {
  struct SSLHandle *handle;
  check(handle = malloc(sizeof(struct SSLHandle)));
  handle->fd = fd;
  handle->ctx = &ssl->ctx;
  memset(&handle->ssn, 0, sizeof(ssl_session));
  ssl_set_bio(&ssl->ctx, net_recv, &handle->fd, net_send, &handle->fd);
  ssl_set_session(&ssl->ctx, 1, 600, &handle->ssn);
  for (int rc;;) {
    rc = ssl_handshake(&ssl->ctx);
    if (!rc) {
      break;
    }
    if (rc == POLARSSL_ERR_NET_WANT_READ) {
      struct pollfd fds = {
        .fd     = fd,
        .events = POLLIN,
      };
      TEMP_FAILURE_RETRY(poll(&fds, 1, -1));
    } else if (rc == POLARSSL_ERR_NET_WANT_WRITE) {
      struct pollfd fds = {
        .fd     = fd,
        .events = POLLOUT,
      };
      TEMP_FAILURE_RETRY(poll(&fds, 1, -1));
    } else {
      check(0);
    }
  }
  return handle;
}

void closeSSLHandle(struct SSLHandle *handle) {
  ssl_close_notify(handle->ctx);
  free(handle);
}

ssize_t SSLread(struct SSLHandle *handle, char *buf, size_t sz) {
  int rc = ssl_read(handle->ctx, (unsigned char *)buf, sz);
  if (rc == POLARSSL_ERR_NET_WANT_READ) {
    errno = EAGAIN;
    return -1;
  } else if (rc == POLARSSL_ERR_NET_CONN_RESET) {
    return 0;
  } else if (rc < 0) {
    errno = EIO;
    return -1;
  }
  return rc;
}

ssize_t SSLwrite(struct SSLHandle *handle, const char *buf, size_t sz) {
  int rc = ssl_write(handle->ctx, (unsigned char *)buf, sz);
  if (rc == POLARSSL_ERR_NET_WANT_WRITE) {
    errno = EAGAIN;
    return -1;
  } else if (rc < 0) {
    errno = EIO;
    return -1;
  }
  return rc;
}

#endif  // USE_POLAR
